#include "muduo/proto/codec.h"
#include "muduo/proto/dispatcher.h"
#include "protobuf.pb.h"
#include "muduo/base/Logging.h"
#include "muduo/base/Mutex.h"
#include "muduo/net/EventLoopThread.h"
#include "muduo/net/TcpClient.h"
#include "muduo/base/CountDownLatch.h"
#include "muduo/net/TcpConnection.h"
#include <iostream>
#include <functional>
#include <string>
using namespace std;
using namespace placeholders;

class Client
{
    typedef std::shared_ptr<google::protobuf::Message> MessagePtr;
    typedef std::shared_ptr<ns_cr::TranslateRsp> TranslateRspPtr;
    typedef std::shared_ptr<ns_cr::AddRsp> AddRspPtr;

public:
    Client(string s_ip, int s_port)
        : _latch(1), // 只有=1的时候调用wait才会阻塞
          _clt(_loopthrad.startLoop(),
               muduo::net::InetAddress(s_ip, s_port), "Client"),
          _dispatcher(std::bind(&Client::unkonwnmessage, this, _1, _2, _3)),
          _codec(std::bind(&ProtobufDispatcher::onProtobufMessage, &_dispatcher, _1, _2, _3))
    {
        // 注册_dispatcher业务请求处理函数
        _dispatcher.registerMessageCallback<ns_cr::TranslateRsp>(
            std::bind(&Client::mytranslate, this, _1, _2, _3));

        _dispatcher.registerMessageCallback<ns_cr::AddRsp>(
            std::bind(&Client::myadd, this, _1, _2, _3));

        // 连接回调处理
        _clt.setConnectionCallback(std::bind(&Client::myconnect, this, _1));

        // 设置成协议处理器，在协议处理结束后,其内部会再次调用请求分发器执行对应的回调函数
        _clt.setMessageCallback(std::bind(
            &ProtobufCodec::onMessage, &_codec, _1, _2, _3));
    }

    void connect()
    {
        _clt.connect(); // 异步操作
        _latch.wait();  // 建立连接成功被唤醒
    }

    bool translate_send(const string &mess)
    {
        // 设置protobuf的TranslateReq对象的成员
        ns_cr::TranslateReq req;
        req.set_mes(mess);
        return send(&req);
    }
    bool add_send(const int &num1, const int &num2)
    {
        // 设置protobuf的AddReq对象的成员
        ns_cr::AddReq req;
        req.set_num1(num1);
        req.set_num2(num2);
        return send(&req);
    }

private:
    // 数据发送函数
    bool send(const google::protobuf::Message *msg)
    {
        // 只有成功建立连接才能进行send
        if (_conn->connected())
        {
            // 序列化发送
            _codec.send(_conn, *msg);
            return true;
        }
        return false;
    }

    // 翻译结果处理函数
    void mytranslate(const muduo::net::TcpConnectionPtr &conn,
                     const TranslateRspPtr &message, muduo::Timestamp)
    {
        cout << "翻译结果: " << message->mes() << endl;
    }

    // 加法结果处理函数
    void myadd(const muduo::net::TcpConnectionPtr &conn,
               const AddRspPtr &message, muduo::Timestamp)
    {
        cout << "相加结果: " << message->result() << endl;
    }

    // 连接成功建立的回调函数
    void myconnect(const muduo::net::TcpConnectionPtr &conn)
    {
        if (conn->connected())
        {
            _latch.countDown(); // 唤醒
            _conn = conn;
        }
        else
        {
            conn->shutdown();
            cout << "连接关闭" << endl;
        }
    }
    // 收到未知响应后的回调处理函数
    void unkonwnmessage(const muduo::net::TcpConnectionPtr &conn,
                        const MessagePtr &message, muduo::Timestamp)
    {
        LOG_INFO << "未知信息: " << message->GetTypeName();
        conn->shutdown();
    }

private:
    muduo::CountDownLatch _latch;           // 控制同步（请求连接是异步的）
    muduo::net::EventLoopThread _loopthrad; // 用于创建线程进行epoll事件关心
    muduo::net::TcpClient _clt;             // 用于回调事件就绪后的请求
    muduo::net::TcpConnectionPtr _conn;     // 用于数据请求发送
    ProtobufDispatcher _dispatcher;         // 请求分发器-注册请求处理函数
    ProtobufCodec _codec;                   // 协议处理器-将收到的请求数据进行协议处理
};

int main()
{
    Client clt("127.0.0.1", 6666);
    clt.connect(); // 建立连接

    while (1)
    {
        clt.add_send(3, 7);
        clt.translate_send("向前");
        sleep(1); // 防止服务端请求还未处理完请求，客户端就退出了
    }

    return 0;
}